from functools import reduce
from typing import Any, List
from collections import defaultdict

class JsonSearch():
    def __init__(self, object) -> None:
        self.json_object = object
        self.value_list = []

    def __search_value(self, json_object, key: str) -> List:
        for k in json_object:
            if (k == key):
                self.value_list.append(json_object[k])

            if (isinstance(k, dict)):
                self.__search_value(k, key)
            elif (isinstance(json_object[k], dict)):
                self.__search_value(json_object[k], key)

            elif (isinstance(k, list)):
                for item in k:
                    if (isinstance(item, dict)):
                        self.__search_value(item, key)
            elif (isinstance(json_object[k], list)):
                for item in json_object[k]:
                    if (isinstance(item, dict)):
                        self.__search_value(item, key)
        return

    def search_value(self, key: str):
        self.value_list = []
        self.__search_value(self.json_object, key)
        return self.value_list


def flatten(items: list):
    for x in items:
        if isinstance(x, list):
            yield from flatten(x)
        else:
            yield x


def merge_dict_list(data: list):
    merged_data = defaultdict(list)
    for item in data:
        for key, value in item.items():
            if (isinstance(value, list)):
                merged_data[key].extend(value)
            else:
                merged_data[key].append(value)
    return [{key: values} for key, values in merged_data.items()]


def search_values(obj, key: str = None, accurate: list = []):
    if (accurate):
        value_list = []
        for sub_obj in obj:
            key_value = JsonSearch(sub_obj).search_value(key)
            if (key_value):
                accurate_value = list(
                    map(lambda x: sub_obj.get(x), accurate))
                accurate_value = reduce(
                    lambda x, y: str(x)+'_'+str(y), accurate_value)
                value_list.append({accurate_value: key_value})
        return merge_dict_list(value_list)
    else:
        return JsonSearch(obj).search_value(key)
        
